import { defineComponent, Ref, ref } from 'vue';
import axios from 'axios';
import { Util } from 'ibz-core';
import qs from 'qs';

/**
 * 上传组件编辑器输入属性
 *
 * @memberof AppUploadProps
 */
const AppUploadProps = {
  /**
   * 双向绑定值
   *
   * @type {String}
   * @memberof AppUploadProps
   */
  value: {
    type: String,
  },

  /**
   * 上下文data数据（表单数据）
   *
   * @type {Object}
   * @memberof AppUploadProps
   */
  contextData: Object,

  /**
   * 上下文
   *
   * @type {Object}
   * @memberof AppUploadProps
   */
  context: Object,

  /**
   * 视图参数
   *
   * @type {Object}
   * @memberof AppUploadProps
   */
  viewParam: Object,

  /**
   * 是否禁用
   *
   * @type {boolean}
   * @memberof AppUploadProps
   */
  disabled: {
    type: Boolean,
    default: false,
  },

  /**
   * 只读状态
   *
   * @type {boolean}
   * @memberof AppUploadProps
   */
  readonly: {
    type: Boolean,
    default: false,
  },

  /**
   * 是否多选
   *
   * @type {boolean}
   * @memberof AppUploadProps
   */
  multiple: {
    type: Boolean,
    default: false,
  },

  /**
   * 文件后缀
   *
   * @type {String}
   * @memberof AppUploadProps
   */
  fileSuffix: String,

  /**
   * 最小文件数量
   *
   * @type {String}
   * @memberof AppUploadProps
   */
  minCount: Number,

  /**
   * 最大文件大小
   *
   * @type {String}
   * @memberof AppUploadProps
   */
  maxSize: Number,

  /**
   * 最大允许上传个数
   *
   * @type {String}
   * @memberof AppUploadProps
   */
  limit: {
    type: Number,
    default: 9999,
  },

  /**
   * 允许上传的文件类型
   *
   * @type {String}
   * @memberof AppUploadProps
   */
  accept: {
    type: String,
    default: '*',
  },

  /**
   * 图片选取模式
   *
   * @type {String}
   * @memberof AppUploadProps
   */
  capture: String,

  /**
   * 上传类型
   *
   * @type {String}
   * @memberof AppUploadProps
   */
  uploadType: {
    type: String,
    default: 'file',
  },

  /**
   * 上传参数
   *
   * @type {Object}
   * @memberof AppUploadProps
   */
  uploadParam: {
    type: Object,
    default: () => {},
  },

  /**
   * 下载参数
   *
   * @type {Object}
   * @memberof AppUploadProps
   */
  exportParam: {
    type: Object,
    default: () => {},
  },
};

/**
 * 文件上传组件
 *
 * @class AppUpload
 */
export const AppUpload = defineComponent({
  name: 'AppUpload',
  props: AppUploadProps,
  emits: ['editorValueChange'],
  methods: {
    /**
     * @description 数据处理
     */
    dataProcess(): void {
      const { context: uploadContext, param: uploadParam } = Util.computedNavData(
        this.contextData,
        this.context,
        this.viewParam,
        this.uploadParam,
      );
      const { context: exportContext, param: exportParam } = Util.computedNavData(
        this.contextData,
        this.context,
        this.viewParam,
        this.exportParam,
      );
      const uploadContextStr: string = qs.stringify(uploadContext, { delimiter: '&' });
      const uploadParamStr: string = qs.stringify(uploadParam, { delimiter: '&' });
      if (!Object.is(uploadContextStr, '') || !Object.is(uploadParamStr, '')) {
        const uploadUrl = `${this.uploadUrl}?${uploadContextStr}&${uploadParamStr}`;
        this.uploadUrl = uploadUrl;
      }
      this.files.forEach((file: any) => {
        if (process.env.NODE_ENV === 'development') {
          const index = this.devFiles.findIndex((devFile: any) => Object.is(devFile.id, file.id));
          if (index !== -1) {
            file.url = this.devFiles[index].url;
            file.isImage = true;
          }
        }
        let downloadUrl = `${this.downloadUrl}/${file.id}`;
        const exportContextStr: string = qs.stringify(exportContext, { delimiter: '&' });
        const exportParamStr: string = qs.stringify(exportParam, { delimiter: '&' });
        if (!Object.is(exportContextStr, '') || !Object.is(exportParamStr, '')) {
          downloadUrl = `${downloadUrl}?${exportContextStr}&${exportParamStr}`;
        }
        file.url = downloadUrl;
      });
    },

    /**
     * @description 文件上传之前
     * @param {*} event 事件源
     */
    async beforeFileUpload(event: any) {
      const { files } = event.target as HTMLInputElement;
      if (files && files.length > 0) {
        if (files.length + this.files.length > this.limit) {
          App.getNoticeService().warning(
            `${(this as any).$tl('common.upload.uploadlimitamount', '上传限制数量为 ')} ${this.limit}`,
          );
          return;
        }
        for (let i = 0; i < files.length; i++) {
          if (!Object.is('file', this.uploadType) && files[i].type.indexOf('image/') == -1) {
            App.getNoticeService().error(
              `${files[i].name} ${(this as any).$tl('common.upload.notpicture', '不是图片')}`,
            );
            return;
          }
          if (this.maxSize) {
            if (files[i].size <= this.maxSize) {
              await this.onFileUpload(files[i]);
            } else {
              App.getNoticeService().error(
                `${files[i].name} ${(this as any).$tl('common.upload.sizeover', '大小超出')} ${this.maxSize}`,
              );
            }
          } else {
            await this.onFileUpload(files[i]);
          }
        }
      }
    },

    /**
     * @description 文件上传
     * @param {*} file 文件
     */
    async onFileUpload(file: any) {
      if (App.isPreviewMode()) {
        if (Object.is('file', this.uploadType)) {
          this.files.push({
            name: file.name,
            id: Util.createUUID(),
          });
          this.showUploadButton = this.files.length >= this.limit ? false : true;
        } else {
          const reader = new FileReader();
          reader.readAsDataURL(file);
          reader.onload = (e: any) => {
            this.files.push({
              url: e.target.result,
              id: Util.createUUID(),
            });
            this.showUploadButton = this.files.length >= this.limit ? false : true;
          };
        }
        return;
      }
      const params = new FormData();
      params.append('file', file, file.name);
      const config = {
        headers: {
          'Content-Type': 'multipart/form-data',
        },
      };
      const response: any = await axios.post(this.uploadUrl, params, config);
      if (response && response.data && response.status === 200) {
        this.onSuccess(response, file);
      } else {
        this.onError(response, file);
      }
    },

    /**
     * @description 删除文件
     * @param {*} file 文件
     */
    onDelete(file: any) {
      if (App.isPreviewMode()) {
        const index = this.files.findIndex((item: any) => Object.is(item.id, file.id));
        if (index != -1) {
          this.files.splice(index, 1);
        }
        return;
      }
      const arr: Array<any> = [];
      this.files.forEach((_file: any) => {
        if (_file.id != file.id) {
          arr.push({ name: _file.name, id: _file.id });
        }
      });
      const value: any = arr.length > 0 ? JSON.stringify(arr) : null;
      this.$emit('editorValueChange', value);
    },

    /**
     * @description 下载
     * @param {*} file 文件
     */
    onDownload(file: any) {
      window.open(file.url);
    },

    /**
     * @description 成功
     * @param {*} response 响应
     * @param {*} file 上传文件
     */
    onSuccess(response: any, file: any) {
      const data = { name: response.filename, id: response.fileid };
      if (process.env.NODE_ENV === 'development') {
        this.devFiles.push(
          Object.assign({}, data, {
            url: file.content,
          }),
        );
      }
      const arr: Array<any> = [];
      this.files.forEach((_file: any) => {
        arr.push({ name: _file.name, id: _file.id });
      });
      arr.push(data);
      const value: any = arr.length > 0 ? JSON.stringify(arr) : null;
      this.$emit('editorValueChange', value);
    },

    /**
     * @description 上传失败
     * @param {*} error 错误
     * @param {*} file 文件
     */
    onError(error: any, file: any) {
      App.getNoticeService().error(`${file.name} ${(this as any).$tl('common.upload.uploadfailure', '上传失败')}`);
    },

    /**
     * @description 绘制文件上传
     * @return {*}
     */
    renderFileUpload() {
      return (
        <div class='app-file-upload'>
          {this.files?.length > 0 ? (
            <ion-item-group class='app-file-items'>
              {this.files.map((file: any) => {
                return (
                  <ion-item class='app-file-item'>
                    <ion-label>
                      <a class='file' onClick={() => this.onDownload(file)}>
                        {file.name}
                      </a>
                    </ion-label>
                    <app-icon name='close-outline' onClick={() => this.onDelete(file)} />
                  </ion-item>
                );
              })}
            </ion-item-group>
          ) : this.readonly ? (
            `${(this as any).$tl('common.upload.nofile', '没有文件')}`
          ) : null}
          <ion-row>
            {!this.readonly && this.showUploadButton ? (
              <ion-button disabled={this.disabled}>
                <app-icon name='add'></app-icon>
                {`${(this as any).$tl('common.upload.uploadfile', '上传文件')}`}
                <input
                  type='file'
                  class='file-upload'
                  accept={this.accept}
                  multiple={this.multiple}
                  disabled={this.disabled}
                  onChange={($event: any) => {
                    this.beforeFileUpload($event);
                  }}
                />
              </ion-button>
            ) : null}
          </ion-row>
        </div>
      );
    },

    /**
     * @description 绘制图片上传
     * @return {*}
     */
    renderPictureUpload() {
      return (
        <div class='app-picture-upload'>
          {this.files?.length > 0
            ? this.files.map((file: any) => {
                return (
                  <div class='app-picture-container picture-preview'>
                    <app-icon name='close-outline' onClick={() => this.onDelete(file)} />
                    <img src={file.url}></img>
                  </div>
                );
              })
            : this.readonly
            ? `${(this as any).$tl('common.upload.nopicture', '没有图片')}`
            : null}
          {!this.readonly && this.showUploadButton ? (
            <div class='app-picture-container'>
              <app-icon name='add'></app-icon>
              <input
                type='file'
                class='file-upload'
                accept={this.accept}
                multiple={this.multiple}
                disabled={this.disabled}
                onChange={($event: any) => {
                  this.beforeFileUpload($event);
                }}
              />
            </div>
          ) : null}
        </div>
      );
    },
  },

  setup(props: any) {
    const environment = App.getEnvironment();
    const uploadUrl: string = `${environment?.UploadFile}`;
    const downloadUrl: string = `${environment?.ExportFile}`;
    const devFiles: any[] = [];
    const files: Ref<any[]> = ref(props.value ? JSON.parse(props.value) : []);
    const showUploadButton: Ref<Boolean> = ref(files.value.length >= props.limit ? false : true);
    return {
      uploadUrl,
      downloadUrl,
      files,
      devFiles,
      showUploadButton,
    };
  },

  mounted() {
    this.dataProcess();
  },

  render() {
    return (
      <div class='app-upload'>
        {Object.is('file', this.uploadType) ? this.renderFileUpload() : this.renderPictureUpload()}
      </div>
    );
  },
});
